#include "PageCache.h"
#include "CentralCache.h"
#include "Common.h"

PageCache PageCache::_sInst;// 饿汉模式，类加载时就创建instance对象
//PageCache PageCache::_sInst;

Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	if (k > NPAGES - 1)
	{
		void* ptr = SystemAlloc(k);
		//Span* span = new Span;
		Span* span = _objectPool.New();

		span->_n = k;
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;

		_idSpanMap[span->_pageId] = span;
		return span;
	}
	if (!_spanLists[k].Empty())
	{
		Span* kSpan = _spanLists[k].PopFront();

		// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; ++i)
		{
			_idSpanMap[kSpan->_pageId + i] = kSpan;
		}
		return kSpan;
	}
	for (int i = k + 1; i < NPAGES; i++)
	{
		if (!_spanLists[i].Empty())
		{
			// 将这个Span切分k和n-k页
			Span* nSpan = _spanLists[i].PopFront();
			//Span* kSpan = new Span;
			Span* kSpan = _objectPool.New();
			//Span* kSpan = nullptr;

			//kSpan->_freeList = nSpan->_freeList;// 本层只初始化_pageId和_n, 此时不初始化_freeList,留到下一层初始化
			kSpan->_n = k;
			kSpan->_pageId = nSpan->_pageId;

			nSpan->_n -= k;
			nSpan->_pageId += k;

			_spanLists[nSpan->_n].PushFront(nSpan);
			// 建立pageId与span*的映射关系
			// 对于kSpan，要将首尾与其中间所有pageId都与Span*映射
			for (size_t i = 0; i < kSpan->_n; i++)
			{
				_idSpanMap[kSpan->_pageId + i] = kSpan;
			}
			// 对于nSpan，只将首尾pageId与Span*映射即可
			_idSpanMap[nSpan->_pageId] = nSpan;
			_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;

			return kSpan;
		}
	}

	void* ptr = SystemAlloc(NPAGES - 1);
	//Span* bigSpan = new Span;
	Span* bigSpan = _objectPool.New();
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1;
	_spanLists[bigSpan->_n].PushFront(bigSpan);
	return NewSpan(k);
}

Span* PageCache::MapObjectToSpan(void* ptr)
{
	PAGE_ID pageId = (PAGE_ID(ptr)) >> PAGE_SHIFT;
	auto it = _idSpanMap.find(pageId);// find代替[],防止误插入
	if (it != _idSpanMap.end())
	{
		return it->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}
}

void PageCache::ReleaseSpanToPageCache(Span* span)
{
	if (span->_n > NPAGES - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_objectPool.Delete(span);
		return;
	}
	// 先向前回收
	while (true)
	{
		PAGE_ID prevPageId = span->_pageId - 1;
		auto it = _idSpanMap.find(prevPageId);
		if (it == _idSpanMap.end()) break;
		Span* prevSpan = it->second;
		if (prevSpan->_isUsing == true) break;// 判断prevSpan是否正在被使用，其实就是判断其是否在PageCache中， 不用_useCount==0是因为担心prevSpan被其他线程同时使用
		if (prevSpan->_n + span->_n > NPAGES - 1) break;

		span->_pageId = prevSpan->_pageId;
		span->_n += prevSpan->_n;

		_spanLists[prevSpan->_n].Erase(prevSpan);// span是struct，本项目未为其集成push/析构函数
		//delete prevSpan;
		_objectPool.Delete(prevSpan);
	}
	// 向后回收
	while (true)
	{
		size_t nextPageId = span->_pageId + span->_n;
		auto it = _idSpanMap.find(nextPageId);
		if (it == _idSpanMap.end()) break;
		Span* nextSpan = it->second;
		if (nextSpan->_isUsing == true) break;// 判断prevSpan是否正在被使用，其实就是判断其是否在PageCache中， 不用_useCount==0是因为担心prevSpan被其他线程同时使用
		if (nextSpan->_n + span->_n > NPAGES - 1) break;

		span->_n += nextSpan->_n;

		_spanLists[nextSpan->_n].Erase(nextSpan);// span是struct，本项目未为其集成push/析构函数
		//delete nextSpan;
		_objectPool.Delete(nextSpan);

	}
	// 最后将span插入到PageCache中
	_spanLists[span->_n].PushFront(span);
	span->_isUsing = false;
	_idSpanMap[span->_pageId] = span;
	_idSpanMap[span->_pageId + span->_n - 1] = span;
}

